import random
from ctypes import windll, byref
from ctypes.wintypes import HWND, POINT
import string
import time
import win32api
import win32con, win32gui, win32ui
import cv2
import numpy as np
import base64
import  operator
import requests
import easyocr


def base642Str(imgBytes):
    base64_data = base64.b64encode(imgBytes)
    return base64_data.decode('utf-8')


scale = 1

from PIL import Image


def getPointOnLine(x1, y1, x2, y2, n):
    """
        鼠标坐标集合移动算法
    """
    x = ((x2 - x1) * n) + x1
    y = ((y2 - y1) * n) + y1
    return int(round(x)), int(round(y))


class win():

    def __init__(self, hwnd):
        self.reader = easyocr.Reader(['ch_sim', 'en'])  # 只需要运行一次就可以将模型加载到内存中

        result = []

        def callback(hwnd, res):
            result.append(hwnd)
            return True

        win32gui.EnumChildWindows(hwnd, callback, '')

        self.hwnd = result[0]
        global hwn
        hwn = result[0]

    def hwn(self):
        return hwn


    def window_capture(self, Target, A=[0, 0, 0, 0], value=0.95):

        # 获取句柄窗口的大小

        rctA = win32gui.GetWindowRect(self.hwnd)
        rctA = list(rctA)

        w = rctA[2] - rctA[0]
        h = rctA[3] - rctA[1]
        w_A = A[2] - A[0]
        h_A = A[3] - A[1]

        hwndDC = win32gui.GetWindowDC(self.hwnd)
        mfcDC = win32ui.CreateDCFromHandle(hwndDC)
        saveDC = mfcDC.CreateCompatibleDC()
        saveBitMap = win32ui.CreateBitmap()
        saveBitMap.CreateCompatibleBitmap(mfcDC, w_A, h_A)
        saveDC.SelectObject(saveBitMap)
        w_A = A[2] - A[0]
        h_A = A[3] - A[1]
        saveDC.BitBlt((0, 0), (w_A, h_A), mfcDC, (A[0], A[1]), win32con.SRCCOPY)
        # saveBitMap.SaveBitmapFile(saveDC, "img_Winapi.bmp")
        ###获取位图信息
        bmpinfo = saveBitMap.GetInfo()
        bmpstr = saveBitMap.GetBitmapBits(True)

        ###生成图像
        im_PIL_TEMP = Image.frombuffer('RGB', (bmpinfo['bmWidth'], bmpinfo['bmHeight']), bmpstr, 'raw', 'BGRX', 0, 1)
        img = cv2.cvtColor(np.asarray(im_PIL_TEMP), cv2.COLOR_RGB2BGR)
        target = img

        template = cv2.imread(Target)
        # 获得模板图片的高宽尺寸
        theight, twidth = template.shape[:2]
        # 执行模板匹配，采用的匹配方式cv2.TM_SQDIFF_NORMED
        result = cv2.matchTemplate(target, template, cv2.TM_SQDIFF_NORMED)
        # 归一化处理
        cv2.normalize(result, result, 0, 1, cv2.NORM_MINMAX, -1)
        # 寻找矩阵（一维数组当做向量，用Mat定义）中的最大值和最小值的匹配结果及其位置
        min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
        # 匹配值转换为字符串
        # 对于cv2.TM_SQDIFF及cv2.TM_SQDIFF_NORMED方法min_val越趋近与0匹配度越好，匹配位置取min_loc
        # 对于其他方法max_val越趋近于1匹配度越好，匹配位置取max_loc
        strmin_val = str(min_val)

        # 绘制矩形边框，将匹配区域标注出来
        # min_loc：矩形定点
        # (min_loc[0]+twidth,min_loc[1]+theight)：矩形的宽高
        # (0,0,225)：矩形的边框颜色；2：矩形边框宽度
        cv2.rectangle(target, min_loc, (min_loc[0] + twidth, min_loc[1] + theight), (0, 0, 225), 2)
        # 显示结果,并将匹配值显示在标题栏上
        #cv2.imshow("MatchResult----MatchingValue=" + strmin_val, target)
        #cv2.waitKey()
        #cv2.destroyAllWindows()

        win32gui.DeleteObject(saveBitMap.GetHandle())
        mfcDC.DeleteDC()
        saveDC.DeleteDC()
        win32gui.ReleaseDC(self.hwnd, hwndDC)
        if float(strmin_val) >= -0.05 and float(strmin_val) <= 0.05 and min_loc[0] != 0 and min_loc[1] != 0:

            return min_loc[0] + A[0], min_loc[1] + A[1]
        else:

            return 0, 0

    def window_str(self, A=[0, 0, 0, 0], textType='ft', bor=False, pic_id=None):
        # 获取句柄窗口的大小
        # if pic_id:
        #     json = {'pic_id': pic_id, 'class': 'rxjh'}
        #     requests.post(url='http://47.98.177.143:34971/ImageOCR/', json=json).json()

        rctA = win32gui.GetWindowRect(self.hwnd)
        rctA = list(rctA)

        w = rctA[2] - rctA[0]
        h = rctA[3] - rctA[1]
        w_A = A[2] - A[0]
        h_A = A[3] - A[1]

        hwndDC = win32gui.GetWindowDC(self.hwnd)
        mfcDC = win32ui.CreateDCFromHandle(hwndDC)
        saveDC = mfcDC.CreateCompatibleDC()
        saveBitMap = win32ui.CreateBitmap()
        saveBitMap.CreateCompatibleBitmap(mfcDC, w_A, h_A)
        saveDC.SelectObject(saveBitMap)
        w_A = A[2] - A[0]
        h_A = A[3] - A[1]
        saveDC.BitBlt((0, 0), (w_A, h_A), mfcDC, (A[0], A[1]), win32con.SRCCOPY)
        # saveBitMap.SaveBitmapFile(saveDC, "img_Winapi.bmp")
        ###获取位图信息
        bmpinfo = saveBitMap.GetInfo()
        bmpstr = saveBitMap.GetBitmapBits(True)

        ###生成图像
        im_PIL_TEMP = Image.frombuffer('RGB', (bmpinfo['bmWidth'], bmpinfo['bmHeight']), bmpstr, 'raw', 'BGRX', 0, 1)

        img = cv2.cvtColor(np.asarray(im_PIL_TEMP), cv2.COLOR_RGB2BGR)
        # if bor:
        #     img = image_binarization(img)
        # cv2.imshow('img',img)
        # cv2.waitKey(0)
        # array_bytes = img.tobytes()  # 或者使用img.tostring()

        # 对数组的图片格式进行编码
        # success, encoded_image = cv2.imencode(".png", img)
        #img_bytes = np.array(cv2.imencode('.png', img)[1]).tobytes()
        # 将数组转为bytes

        # img_bytes = encoded_image.tostring()
        result = self.reader.readtext(img)
        if result!=[]:
            return result[0][-2]
        else:
            return ''
    def window_color(self,res,A=[0, 0, 0, 0]):
        # 获取句柄窗口的大小
        # if pic_id:
        #     json = {'pic_id': pic_id, 'class': 'rxjh'}
        #     requests.post(url='http://47.98.177.143:34971/ImageOCR/', json=json).json()

        rctA = win32gui.GetWindowRect(self.hwnd)
        rctA = list(rctA)

        w = rctA[2] - rctA[0]
        h = rctA[3] - rctA[1]
        w_A = A[2] - A[0]
        h_A = A[3] - A[1]

        hwndDC = win32gui.GetWindowDC(self.hwnd)
        mfcDC = win32ui.CreateDCFromHandle(hwndDC)
        saveDC = mfcDC.CreateCompatibleDC()
        saveBitMap = win32ui.CreateBitmap()
        saveBitMap.CreateCompatibleBitmap(mfcDC, w_A, h_A)
        saveDC.SelectObject(saveBitMap)
        w_A = A[2] - A[0]
        h_A = A[3] - A[1]
        saveDC.BitBlt((0, 0), (w_A, h_A), mfcDC, (A[0], A[1]), win32con.SRCCOPY)
        # saveBitMap.SaveBitmapFile(saveDC, "img_Winapi.bmp")
        ###获取位图信息
        bmpinfo = saveBitMap.GetInfo()
        bmpstr = saveBitMap.GetBitmapBits(True)

        ###生成图像
        im_PIL_TEMP = Image.frombuffer('RGB', (bmpinfo['bmWidth'], bmpinfo['bmHeight']), bmpstr, 'raw', 'BGRX', 0, 1)
        img = cv2.cvtColor(np.asarray(im_PIL_TEMP), cv2.COLOR_RGB2BGR)
        for a in range(len(img)):
            for b in range(len(img[0])):
                if all(operator.eq( img[a,b],res)):

                    return  True
                else:
                    return False




    def mouse_move(self, new_x, new_y):
        if new_y is not None and new_x is not None:
            point = (new_x, new_y)
            win32api.SetCursorPos(point)
            self.x = new_x
            self.y = new_y

    def click_point(self, x, y, bor=True):
        if bor:
            x = x +random.randint(10,10)
            y = y +random.randint(10,10)

        win32api.SendMessage(self.hwnd, win32con.WM_LBUTTONDOWN, 0, ((y) << 16 | (x)));
        win32api.SendMessage(self.hwnd, win32con.WM_LBUTTONUP, 0, ((y) << 16 | (x)));

    def send_enter(self):
        win32api.SendMessage(self.hwnd, win32con.WM_KEYDOWN, 74, 0)
        win32api.SendMessage(self.hwnd, win32con.WM_KEYUP, 74, 0)

    def send_str(self, text):
        astrToint = [ord(c) for c in text]
        for item in astrToint:
            win32api.PostMessage(self.hwnd, win32con.WM_CHAR, item, 0)

    def move(self, x1, y1, x2, y2):
        point = win32api.MAKELONG(x1, y1)  # 定义起始点

        point1 = win32api.MAKELONG(x2, y2)  # 定义终点
        print('开始点击')
        win32api.SendMessage(self.hwnd, win32con.WM_LBUTTONDOWN, 0, ((y1) << 16 | (x1)))  # 起始点按住
        print('开始移动')
        time.sleep(1)
        win32gui.SendMessage(self.hwnd, win32con.WM_MOUSEMOVE, win32con.MK_LBUTTON, point1)  # 移动到终点
        time.sleep(1)
        win32gui.SendMessage(self.hwnd, win32con.WM_LBUTTONUP, 0, 0)  # 松开
        time.sleep(1)


def image_binarization(img):
    # 将图片转为灰度图

    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

    # retval, dst = cv2.threshold(gray, 110, 255, cv2.THRESH_BINARY)
    # 最大类间方差法(大津算法)，thresh会被忽略，自动计算一个阈值
    retval, dst = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
    # cv2.imwrite('binary.jpg', dst)
    return dst


def Ocr(self, img, value=None):
    result = []
    resultOcr = self.ocr.ocr(img, cls=True)
    if resultOcr:
        for line in resultOcr:
            if value == line[1][0]:
                result.append([line[0], line[1][0]])
            elif not value:
                result.append([line[0], line[1][0]])
    return result


def findhwnd():
    all_hwnd = []
    all_hwnd = get_all_windows()
    hwnd = []

    for i in all_hwnd:
        hwnds = get_title(i, 'LDPlayerMainFrame')
        if hwnds != None:
            hwnd.append(hwnds)

    return hwnd


def get_all_windows():
    hWnd_list = []
    win32gui.EnumWindows(lambda hWnd, param: param.append(hWnd), hWnd_list)

    return hWnd_list


def get_title(hwnd, name):
    title = win32gui.GetClassName(hwnd)
    if title == name:
        return hwnd


class c_keybord(object):
    def __init__(self, handle: HWND):
        """
         https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
        :type handle: HWND
        :param handle:
        """
        self.handle = handle
        self.PostMessageW = windll.user32.PostMessageW
        self.MapVirtualKeyW = windll.user32.MapVirtualKeyW
        self.VkKeyScanA = windll.user32.VkKeyScanA
        self.WM_KEYDOWN = 0x100
        self.WM_KEYUP = 0x101

        self.VkCode = {
            "back": 0x08,
            "tab": 0x09,
            "return": 0x0D,
            "shift": 0x10,
            "control": 0x11,
            "menu": 0x12,
            "pause": 0x13,
            "capital": 0x14,
            "escape": 0x1B,
            "space": 0x20,
            "end": 0x23,
            "home": 0x24,
            "left": 0x25,
            "up": 0x26,
            "right": 0x27,
            "down": 0x28,
            "print": 0x2A,
            "snapshot": 0x2C,
            "insert": 0x2D,
            "delete": 0x2E,
            "lwin": 0x5B,
            "rwin": 0x5C,
            "numpad0": 0x60,
            "numpad1": 0x61,
            "numpad2": 0x62,
            "numpad3": 0x63,
            "numpad4": 0x64,
            "numpad5": 0x65,
            "numpad6": 0x66,
            "numpad7": 0x67,
            "numpad8": 0x68,
            "numpad9": 0x69,
            "multiply": 0x6A,
            "add": 0x6B,
            "separator": 0x6C,
            "subtract": 0x6D,
            "decimal": 0x6E,
            "divide": 0x6F,
            "f1": 0x70,
            "f2": 0x71,
            "f3": 0x72,
            "f4": 0x73,
            "f5": 0x74,
            "f6": 0x75,
            "f7": 0x76,
            "f8": 0x77,
            "f9": 0x78,
            "f10": 0x79,
            "f11": 0x7A,
            "f12": 0x7B,
            "numlock": 0x90,
            "scroll": 0x91,
            "lshift": 0xA0,
            "rshift": 0xA1,
            "lcontrol": 0xA2,
            "rcontrol": 0xA3,
            "lmenu": 0xA4,
            "rmenu": 0XA5
        }

    def get_virtual_keycode(self, key: str):
        """根据按键名获取虚拟按键码
        https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-vkkeyscana
        Args:
            key (str): 按键名
        Returns:
            int: 虚拟按键码
        """
        if len(key) == 1 and key in string.printable:
            return self.VkKeyScanA(ord(key)) & 0xff
        else:
            return self.VkCode[key]

    def key_down(self, vk_code, scan_code):
        """按下指定按键
            https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-keydown
        Args:
            :param vk_code:
            :param scan_code:
        """

        wparam = vk_code
        lparam = (scan_code << 16) | 1
        self.PostMessageW(self.handle, self.WM_KEYDOWN, wparam, lparam)

    def key_up(self, vk_code, scan_code):
        """放开指定按键
            https://docs.microsoft.com/en-us/windows/win32/inputdev/wm-keyup
        Args:
            :param vk_code:
            :param scan_code:
        """
        wparam = vk_code
        lparam = (scan_code << 16) | 0XC0000001
        self.PostMessageW(self.handle, self.WM_KEYUP, wparam, lparam)

    def key_click(self, key: str, wait=0.2):

        vk_code = self.get_virtual_keycode(key)
        scan_code = self.MapVirtualKeyW(vk_code, 0)
        self.key_down(vk_code, scan_code)
        time.sleep(wait)
        self.key_up(vk_code, scan_code)


def getPointOnLine(x1, y1, x2, y2, n):
    """
        鼠标坐标集合移动算法
    """
    x = ((x2 - x1) * n) + x1
    y = ((y2 - y1) * n) + y1
    return int(round(x)), int(round(y))


class WinMouse(object):
    def __init__(self, handle: int, num_steps=80):
        self.handle = handle
        self.__win32api = win32api
        self.__win32con = win32con
        self.num_steps = num_steps
        self.__width = win32api.GetSystemMetrics(win32con.SM_CXSCREEN)  # 获得屏幕分辨率X轴
        self.__high = win32api.GetSystemMetrics(win32con.SM_CYSCREEN)  # 获得屏幕分辨率Y轴

    def __left_down(self, param):
        self.__win32api.PostMessage(self.handle, self.__win32con.WM_LBUTTONDOWN, self.__win32con.MK_LBUTTON, param)

    def __left_up(self, param):
        self.__win32api.PostMessage(self.handle, self.__win32con.WM_LBUTTONUP, None, param)

    def __left_move(self, param):
        self.__win32api.PostMessage(self.handle, self.__win32con.WM_MOUSEMOVE, self.__win32con.MK_LBUTTON, param)

    def __right_down(self, param):
        self.__win32api.PostMessage(self.handle, self.__win32con.WM_RBUTTONDOWN, self.__win32con.MK_RBUTTON, param)

    def __right_up(self, param):
        self.__win32api.PostMessage(self.handle, self.__win32con.WM_RBUTTONUP, None, param)

    def __right_move(self, param):
        self.__win32api.PostMessage(self.handle, self.__win32con.WM_MOUSEMOVE, self.__win32con.MK_RBUTTON, param)

    def left_click(self, x: int, y: int, wait=0.2):
        param = self.__win32api.MAKELONG(x, y)
        self.__left_down(param=param)
        time.sleep(wait)
        self.__left_up(param=param)

    def left_doubleClick(self, x: int, y: int, click=2, wait=0.4):
        wait = wait / click
        param = self.__win32api.MAKELONG(x, y)
        for cou in range(click):
            self.__left_down(param=param)
            time.sleep(wait)
            self.__left_up(param=param)

    def left_click_move(self, x: int, y: int, m_x: int, m_y: int, wait=2):
        param = self.__win32api.MAKELONG(x, y)
        self.__left_down(param)
        steps = [getPointOnLine(x, y, m_x, m_y, n / self.num_steps) for n in range(self.num_steps)]
        steps.append((m_x, m_y))
        wait_amount = wait / self.num_steps
        new_steps = list(set(steps))
        new_steps.sort(key=steps.index)
        for step in new_steps:
            tweenX, tweenY = step
            param = self.__win32api.MAKELONG(tweenX, tweenY)
            self.__left_move(param)
            time.sleep(wait_amount)
        # param = self.__win32api.MAKELONG(m_x, m_y)
        # self.__left_move(param)
        # time.sleep(wait)

        self.__left_up(param)

    def right_click(self, x: int, y: int, wait=0.2):
        param = self.__win32api.MAKELONG(x, y)
        self.__right_down(param=param)
        time.sleep(wait)
        self.__right_up(param=param)

    def right_doubleClick(self, x: int, y: int, click=2, wait=0.4):
        wait = wait / click
        param = self.__win32api.MAKELONG(x, y)
        for cou in range(click):
            self.__right_down(param=param)
            time.sleep(wait)
            self.__right_up(param=param)

    def right_click_move(self, x: int, y: int, m_x: int, m_y: int, wait=2):
        param = self.__win32api.MAKELONG(x, y)
        self.__right_down(param)
        steps = [getPointOnLine(x, y, m_x, m_y, n / self.num_steps) for n in range(self.num_steps)]
        steps.append((m_x, m_y))
        wait_amount = wait / self.num_steps
        new_steps = list(set(steps))
        new_steps.sort(key=steps.index)
        for step in new_steps:
            tweenX, tweenY = step
            param = self.__win32api.MAKELONG(tweenX, tweenY)
            self.__right_move(param)
            time.sleep(wait_amount)
        self.__right_up(param)
